package com.gethistorics.components;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseWheelEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;


public class ImageTileSequence extends Component {
   private int tileWidth = 100;
   private int tileHeight = 100;
   private int hpadd = (int)(tileWidth * 0.05f);
   private int vpadd = (int)(tileHeight * 0.05f);
   
   private BufferedImage emptyTile;
   private BufferedImage[] imageTiles;

   private boolean bufferInitialized = false;
   private Graphics bufferedGraphics;
   private Image background, offscreen;
   private BufferedImage avatar;
   private float zoomLevel = 0.7f;
   private float desiredZoomLevel = zoomLevel;
   private final int motionTime = 4000;//ms
   private long zoomEndTime;
   
   /* now that the screen is full size, paint the background on it */
   private Color gradientStart = Color.BLACK;
   private Color gradientEnd = Color.DARK_GRAY;//.DARK_GRAY;
 
   
   private boolean motionThreadSuspended = false;
   class MyThread extends Thread {
      @Override
      public void run() {
         long now = System.currentTimeMillis();
         while(true){
            while(zoomLevel!=desiredZoomLevel){
               int direction = desiredZoomLevel>zoomLevel?1:-1;
               zoomLevel += direction * 0.02f;
               
               if((direction<0 && zoomLevel<desiredZoomLevel) ||
                     (direction>0 && zoomLevel>desiredZoomLevel)) zoomLevel = desiredZoomLevel;
               
               repaint();
               //paintBuffer();
               try {
                  sleep(33);
               } catch (InterruptedException e) {
                  e.printStackTrace();
               }
               now = System.currentTimeMillis();
            }
            motionThreadSuspended = true;
            synchronized(this){
               try {
                  while(motionThreadSuspended) wait();
               } catch (InterruptedException e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
               }
            }
         }
      }
      
   };
   private MyThread motionThread = new MyThread();
   
   public ImageTileSequence(int tw, int th){
      setTileWidth(tw);
      setTileHeight(th);
      setNumTiles(3);
      addMouseWheelListener(new MouseAdapter(){
         @Override
         public void mouseWheelMoved(MouseWheelEvent e) {
            desiredZoomLevel += -e.getWheelRotation() * 0.1;//0.05;
            if(desiredZoomLevel<0.1f) desiredZoomLevel = 0.1f;
            if(desiredZoomLevel>1.5) desiredZoomLevel = 1.5f;
            System.out.println(desiredZoomLevel);
            
            synchronized(motionThread){
               motionThreadSuspended = false;
               motionThread.notify();
            }
         }
         
      });
      motionThread.start();
   }
   
   public ImageTileSequence(int tw, int th, int numTiles){
      this(tw, th);
      setNumTiles(numTiles);
   }
   
   public void render(){

      bufferedGraphics = offscreen.getGraphics();
   }
   
   public void setTile(int index, BufferedImage tile){
      imageTiles[index] = tile;
   }
   public BufferedImage getTile(int index){
      return imageTiles[index];
   }
   public BufferedImage[] getTiles(){
      return imageTiles;
   }
   public void clearTiles(){
      imageTiles = null;
   }
   public void setNumTiles(int numTiles){
      imageTiles = new BufferedImage[numTiles];
   }

   public void initializeBuffer(){
      background = createImage(getWidth(), getHeight());
      offscreen = createImage(getWidth(), getHeight());
      if(background==null || offscreen==null) return;
      
      Graphics2D g2 = (Graphics2D)background.getGraphics();
      bufferedGraphics = offscreen.getGraphics();
      
      GradientPaint painter = new GradientPaint(0, 0, gradientEnd, 0, getHeight() / 2, gradientStart);
      g2.setPaint(Color.black);//painter);
      Rectangle2D rect = new Rectangle2D.Double(0, 0, getWidth(), getHeight() / 2.0);
      g2.fill(rect);
      
      painter = new GradientPaint(0, getHeight() / 2, gradientStart, 0, getHeight(), gradientEnd);
      g2.setPaint(painter);
      rect = new Rectangle2D.Double(0, (getHeight() / 2.0) - 1.0, getWidth(), getHeight());
      g2.fill(rect);
      setupImage();
      bufferInitialized = true;
   }
   
   /* (non-Javadoc)
    * @see java.awt.Container#paint(java.awt.Graphics)
    */
   @Override
   public void paint(Graphics g) {
      System.out.println("painting");
      if(!bufferInitialized) initializeBuffer();
      
      //paint the background image
      bufferedGraphics.clearRect(0, 0, getWidth(), getHeight());
      bufferedGraphics.drawImage(background, 0, 0, getWidth(), getHeight(), this);
      
      int avWidth = (int)(avatar.getWidth() * zoomLevel);
      int avHeight = (int)(avatar.getHeight() * zoomLevel);
      
      bufferedGraphics.drawImage(avatar, 
            getWidth()/2 - avWidth/2, 
            getHeight()/2 - avHeight/3, 
            avWidth, 
            avHeight, 
            this);
      bufferedGraphics.drawImage(avatar, 
            getWidth()/2 - avWidth/2 - avWidth - 10, 
            getHeight()/2 - avHeight/3, 
            avWidth, 
            avHeight, 
            this);
      bufferedGraphics.drawImage(avatar, 
            getWidth()/2 + avWidth/2 + 10, 
            getHeight()/2 - avHeight/3, 
            avWidth, 
            avHeight, 
            this);
      
      g.drawImage(offscreen, 0, 0, offscreen.getWidth(this), offscreen.getHeight(this), this);
   }
   
   private void setupImage(){
      Image im = Utils.getImage("tc.png");
      float imageAspect = (float)im.getWidth(this)/(float)im.getHeight(this);
      float tileAspect = (float)tileWidth/(float)tileHeight;
      
      int w = -1;
      int h = -1;
      if(imageAspect>tileAspect) w = tileWidth;
      else h = tileHeight * 2 + vpadd;
      
      im = im.getScaledInstance(w, h, Image.SCALE_SMOOTH);
      BufferedImage deal = convertToBufferedImage(im, BufferedImage.TYPE_INT_ARGB);
      
      avatar = new BufferedImage(deal.getWidth(), deal.getHeight() * 2 + (int)(deal.getHeight() * 0.05f), BufferedImage.TYPE_INT_ARGB);
      
      Graphics g = avatar.getGraphics();
      Graphics2D g2 = (Graphics2D)g;
      
            
      int avatarWidth = deal.getWidth();
      int avatarHeight = deal.getHeight();
      System.out.println("w=" + avatarWidth + ", h=" + avatarHeight);
      
      
      BufferedImage reflection = new BufferedImage(avatarWidth, avatarHeight, BufferedImage.TYPE_INT_ARGB);
      Graphics2D reflectionGraphics = reflection.createGraphics();

      AffineTransform tranform = AffineTransform.getScaleInstance(1.0, -1.0);
      tranform.translate(0, -avatarHeight);
      // draw the flipped image
      reflectionGraphics.drawImage(deal, tranform, this);

      GradientPaint gpainter = new GradientPaint(0.0f, 0.0f,
      new Color(0.0f, 0.0f, 0.0f, 0.5f),
      0.0f, avatarHeight / 2.0f,
      new Color(0.0f, 0.0f, 0.0f, 1.0f));

      // this use : Ar = Ad*(1-As) and Cr = Cd*(1-As)
      reflectionGraphics.setComposite(AlphaComposite.DstOut);
      reflectionGraphics.setPaint(gpainter);
      // this will make our image transluent ...
      reflectionGraphics.fill(new Rectangle2D.Double(0, 0, avatarWidth, avatarHeight));

      reflectionGraphics.dispose();
      g.drawImage(deal, 0, 0, this);
      g.drawImage(reflection, 0, avatarHeight + (int)(avatarHeight * 0.05f), this);

   }
   
   @Override
   public void update(Graphics g){
      //Don't clear the screen...
      System.out.println("Update called");
      //paint(g);
   }
   
   private BufferedImage createReflectedPicture(BufferedImage avatar) {
      int avatarWidth = avatar.getWidth();
      int avatarHeight = avatar.getHeight();

      BufferedImage gradient = createGradientMask(avatarWidth, avatarHeight);
      BufferedImage buffer = createReflection(avatar,
                                              avatarWidth, avatarHeight);

      applyAlphaMask(gradient, buffer, avatarWidth, avatarHeight);

      return buffer;
  }
   
   public static BufferedImage convertToBufferedImage(Image img, int type)
   {             
      int wid = img.getWidth( null );
      int ht = img.getHeight( null );
      BufferedImage bi = new BufferedImage(wid, ht, type);
      Graphics2D g2d = bi.createGraphics();      
      g2d.drawImage(img, 0,0,wid, ht, null);             
      return bi;   
   }

   
   private BufferedImage createGradientMask(int avatarWidth, int avatarHeight) {
      BufferedImage gradient = new BufferedImage(avatarWidth, avatarHeight,
                                                 BufferedImage.TYPE_INT_ARGB);
      Graphics2D g = gradient.createGraphics();
      GradientPaint painter = new GradientPaint(0.0f, 0.0f,
                                                new Color(1.0f, 1.0f, 1.0f, 0.5f),
                                                0.0f, avatarHeight / 2.0f,
                                                new Color(1.0f, 1.0f, 1.0f, 0.0f));
      g.setPaint(painter);
      g.fill(new Rectangle2D.Double(0, 0, avatarWidth, avatarHeight));
      
      g.dispose();
      gradient.flush();

      return gradient;
  }
   
   private BufferedImage createReflection(BufferedImage avatar,
         int avatarWidth,
         int avatarHeight) {

      BufferedImage buffer = new BufferedImage(avatarWidth, avatarHeight << 1,
                     BufferedImage.TYPE_INT_ARGB);
      Graphics2D g = buffer.createGraphics();
      
      g.drawImage(avatar, null, null);
      g.translate(0, avatarHeight << 1);
      
      AffineTransform reflectTransform = AffineTransform.getScaleInstance(1.0, -1.0);
      g.drawImage(avatar, reflectTransform, null);
      g.translate(0, -(avatarHeight << 1));
      
      g.dispose();
      
      return buffer;
   }
   
   private void applyAlphaMask(BufferedImage gradient,
         BufferedImage buffer,
         int avatarWidth, int avatarHeight) {

      Graphics2D g2 = buffer.createGraphics();
      g2.setComposite(AlphaComposite.DstOut);
      g2.drawImage(gradient, null, 0, avatarHeight);
      g2.dispose();
   }

   /**
    * @return the tileWidth
    */
   public int getTileWidth() {
      return tileWidth;
   }

   /**
    * @param tileWidth the tileWidth to set
    */
   public void setTileWidth(int tileWidth) {
      this.tileWidth = tileWidth;
      hpadd = (int)(tileWidth * 0.05f);
   }

   /**
    * @return the tileHeight
    */
   public int getTileHeight() {
      return tileHeight;
   }

   /**
    * @param tileHeight the tileHeight to set
    */
   public void setTileHeight(int tileHeight) {
      this.tileHeight = tileHeight;
      vpadd = (int)(tileWidth * 0.05f);
   }
   
   
   
}
